package com.ccx.demo.business.common.dao.jpa;

import com.ccx.demo.business.common.entity.QTabErrorLog;
import com.ccx.demo.business.common.entity.TabErrorLog;
import com.google.common.collect.ObjectArrays;
import com.google.common.collect.Sets;
import com.querydsl.core.QueryResults;
import com.querydsl.core.types.Expression;
import com.querydsl.core.types.Projections;
import com.querydsl.core.types.dsl.BooleanExpression;
import com.support.mvc.entity.IWhere;
import com.support.mvc.entity.base.Page;
import com.utils.enums.Limit;
import com.utils.util.Dates;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.util.CollectionUtils;

import javax.persistence.NonUniqueResultException;
import java.util.*;
import java.util.function.Consumer;
import java.util.stream.Collectors;

import static com.ccx.demo.config.init.AppInit.getQueryFactory;
import static com.utils.util.Dates.Pattern.yyyyMMddHHmmssSSS;

/**
 * 数据操作：异常日志记录
 *
 * @author 谢长春 on 2022-02-16 V20220301
 */

public interface ErrorLogRepository
        extends JpaRepository<TabErrorLog, Long>,
        org.springframework.data.querydsl.QuerydslPredicateExecutor<TabErrorLog> {


    // 每个 DAO 层顶部只能有一个查询实体,且必须以 table 命名,表示当前操作的数据库表. 当 table 作为主表的连接查询方法也必须写在这个类
    QTabErrorLog table = QTabErrorLog.tabErrorLog;

    /**
     * 异常日志记录 新增数据
     * <pre>
     * 注意：
     *   原来通过 AOP 和数据库设置的默认值迁移到这里。
     *   因为 JPA 特性导致新增之后获取不到时间和数据库的默认值，
     *   所以这里把需要有默认值的数据库字段都预设值好，同时兼容不支持设置默认值的数据库
     * </pre>
     *
     * @param userId {@link Long} 操作用户ID
     * @param obj    {@link TabErrorLog} 新增对象
     * @return {@link TabErrorLog}
     */
    default TabErrorLog insert(final Long userId, final TabErrorLog obj) {
        final Dates now = Dates.now();
        obj.insert(now, userId);
        return save(obj);
    }

    /**
     * 异常日志记录 新增数据
     * <pre>
     * 注意：
     *   原来通过 AOP 和数据库设置的默认值迁移到这里。
     *   因为 JPA 特性导致新增之后获取不到时间和数据库的默认值，
     *   所以这里把需要有默认值的数据库字段都预设值好，同时兼容不支持设置默认值的数据库
     * </pre>
     *
     * @param userId {@link Long} 操作用户ID
     * @param list   {@link List<TabErrorLog>} 新增对象
     * @return {@link List<TabErrorLog>}
     */
    default List<TabErrorLog> insert(final Long userId, final List<TabErrorLog> list) {
        final Dates now = Dates.now();
        list.forEach(obj -> obj.insert(now, userId));
        return saveAll(list);
    }

    /**
     * 异常日志记录 更新数据
     *
     * @param id     {@link Long} 数据ID
     * @param userId {@link Long} 操作用户ID
     * @param obj    {@link TabErrorLog} 更新对象
     * @return {@link Long} 更新行数
     */
    // @CacheEvict(cacheNames = ITabErrorLogCache.CACHE_ROW_BY_ID, key = "#id") // 若使用缓存需要解开代码
    default long update(
            final Long id
            , final Long userId
            , final TabErrorLog obj) {
        final BooleanExpression where = table.id.eq(id).and(table.updateTime.eq(obj.getUpdateTime()));
        obj.setUpdateUserId(userId); // 设置更新操作人
        obj.setUpdateTime(yyyyMMddHHmmssSSS.now()); // 设置更新时间
        return obj.update(getQueryFactory().update(table))
                .get()
                .set(table.updateUserId, userId)
                .set(table.updateTime, obj.getUpdateTime())
                .where(where)
                .execute();
    }

//    /**
//     * 异常日志记录 按 id 物理删除
//     *
//     * @param id     {@link Long} 数据id
//     * @param userId {@link Long} 操作用户ID
//     * @return {@link Long} 物理删除行数
//     */
//    // @CacheEvict(cacheNames = ITabErrorLogCache.CACHE_ROW_BY_ID, key = "#id") // 若使用缓存需要解开代码
//    default long deleteById(final Long id, final Long userId) {
//        return getQueryFactory()
//                .delete(table)
//                .where(table.id.eq(id))
//                .execute();
//    }
//
//    /**
//     * 异常日志记录 按 id+updateTime 物理删除
//     *
//     * @param id         {@link Long} 数据id
//     * @param userId     {@link Long} 操作用户ID
//     * @param updateTime {@link String} 数据最后一次更新时间
//     * @return {@link Long} 物理删除行数
//     */
//    // @CacheEvict(cacheNames = ITabErrorLogCache.CACHE_ROW_BY_ID, key = "#id") // 若使用缓存需要解开代码
//    default long deleteById(final Long id, final String updateTime, final Long userId) {
//        return getQueryFactory()
//                .delete(table)
//                .where(table.id.eq(id).and(table.updateTime.eq(updateTime)))
//                .execute();
//    }
//
//    /**
//     * 异常日志记录 按 id 批量物理删除
//     *
//     * @param ids    {@link Set<Long>} 数据 id 集合
//     * @param userId {@link Long} 操作用户ID
//     * @return {@link Long} 物理删除行数
//     */
//    default long deleteByIds(final Set<Long> ids, final Long userId) {
//        return getQueryFactory()
//                .delete(table)
//                .where(table.id.in(ids))
//                .execute();
//    }
//
//    /**
//     * 异常日志记录 按 id+updateTime 批量物理删除
//     *
//     * @param ids         {@link Set<Long>} 数据ID
//     * @param updateTimes {@link Set<String>} 数据最后更新时间
//     * @param userId      {@link Long} 操作用户ID
//     * @return {@link Long} 物理删除行数
//     */
//    default long deleteByIds(final Set<Long> ids, final Set<String> updateTimes, final Long userId) {
//        return getQueryFactory()
//                .delete(table)
//                .where(table.id.in(ids)
//                        .and(CollectionUtils.isEmpty(updateTimes) ? null : table.updateTime.in(updateTimes))
//                )
//                .execute();
//    }
//

    /**
     * 异常日志记录 按 id 逻辑删除
     *
     * @param id     {@link Long} 数据id
     * @param userId {@link Long} 操作用户ID
     * @return {@link Long} 逻辑删除行数
     */
    // @CacheEvict(cacheNames = ITabErrorLogCache.CACHE_ROW_BY_ID, key = "#id") // 若使用缓存需要解开代码
    default long markDeleteById(final Long id, final Long userId) {
        return getQueryFactory()
                .update(table)
                .set(table.deleted, true)
                .set(table.updateUserId, userId)
                .set(table.updateTime, yyyyMMddHHmmssSSS.now()) // 设置更新时间，注意这里有带3位毫秒数，数据库需要设置支持存储3位毫秒
                .where(table.id.eq(id).and(table.deleted.eq(false)))
                .execute();
    }

    /**
     * 异常日志记录 按 id+updateTime 逻辑删除
     *
     * @param id         {@link Long} 数据id
     * @param userId     {@link Long} 操作用户ID
     * @param updateTime {@link String} 数据最后一次更新时间
     * @return {@link Long} 逻辑删除行数
     */
    // @CacheEvict(cacheNames = ITabErrorLogCache.CACHE_ROW_BY_ID, key = "#id") // 若使用缓存需要解开代码
    default long markDeleteById(final Long id, final String updateTime, final Long userId) {
        return getQueryFactory()
                .update(table)
                .set(table.deleted, true)
                .set(table.updateUserId, userId)
                .set(table.updateTime, yyyyMMddHHmmssSSS.now()) // 设置更新时间，注意这里有带3位毫秒数，数据库需要设置支持存储3位毫秒
                .where(table.id.eq(id).and(table.deleted.eq(false)).and(table.updateTime.eq(updateTime)))
                .execute();
    }

    /**
     * 异常日志记录 按 id 批量逻辑删除
     *
     * @param ids    {@link Set<Long>} 数据 id 集合
     * @param userId {@link Long} 操作用户ID
     * @return {@link Long} 逻辑删除行数
     */
    default long markDeleteByIds(final Set<Long> ids, final Long userId) {
        return getQueryFactory()
                .update(table)
                .set(table.deleted, true)
                .set(table.updateUserId, userId)
                .set(table.updateTime, yyyyMMddHHmmssSSS.now()) // 设置更新时间，注意这里有带3位毫秒数，数据库需要设置支持存储3位毫秒
                .where(table.id.in(ids).and(table.deleted.eq(false)))
                .execute();
    }

    /**
     * 异常日志记录 按 id+updateTime 批量逻辑删除
     *
     * @param ids         {@link Set<Long>} 数据ID
     * @param updateTimes {@link Set<String>} 数据最后更新时间
     * @param userId      {@link Long} 操作用户ID
     * @return {@link Long} 逻辑删除行数
     */
    default long markDeleteByIds(final Set<Long> ids, final Set<String> updateTimes, final Long userId) {
        return getQueryFactory()
                .update(table)
                .set(table.deleted, true)
                .set(table.updateUserId, userId)
                .set(table.updateTime, yyyyMMddHHmmssSSS.now()) // 设置更新时间，注意这里有带3位毫秒数，数据库需要设置支持存储3位毫秒
                .where(table.id.in(ids)
                        .and(table.deleted.eq(false))
                        .and(CollectionUtils.isEmpty(updateTimes) ? null : table.updateTime.in(updateTimes))
                )
                .execute();
    }

    /**
     * 异常日志记录 按 条件查询单条数据，如果查询结果超过 1 条数据则抛出异常
     *
     * @param condition {@link TabErrorLog} 查询条件
     * @return {@link Optional<TabErrorLog>} 实体对象
     */
    default Optional<TabErrorLog> findOne(final TabErrorLog condition) {
        return findOne(condition.where());
    }

    /**
     * 异常日志记录 按 条件查询单条数据，如果查询结果超过 1 条数据则抛出异常
     *
     * @param where {@link IWhere.QdslWhere} 查询条件
     * @return {@link Optional<TabErrorLog>} 实体对象
     */
    default Optional<TabErrorLog> findOne(final IWhere.QdslWhere where) {
        final List<TabErrorLog> list = getQueryFactory()
                .selectFrom(table)
                .where(where.toPredicate())
                .limit(Limit.L2.value)
                .fetch();
        if (list.size() > 1) {
            throw new NonUniqueResultException("预期查询结果行数为1，实际查询结果行数大于1");
        }
        return list.stream().findFirst();
    }
//
//    /**
//     * 异常日志记录 按 ID 查询并缓存到redis
//     *
//     * @param id   {@link Long} 数据ID
//     * @return {@link TabErrorLog} 实体对象
//     */
//     @Cacheable(cacheNames = ITabErrorLogCache.CACHE_ROW_BY_ID, key = "#id", unless="#result == null") // 若使用缓存需要解开代码
//     default TabErrorLog findCacheById(final Long id){
//         return findById(id).orElse(null);
//     }
//

    /**
     * 异常日志记录 按 ID 批量查询，以 id 为 key 返回map
     *
     * @param ids {@link Long} 数据ID
     * @return Map<Long, TabErrorLog> Map 对象集合
     */
    default Map<Long, TabErrorLog> mapByIds(final Collection<Long> ids) {
        return findAllById(Sets.newHashSet(ids))
                .stream()
                .collect(Collectors.toMap(TabErrorLog::getId, TabErrorLog::cloneObject));
    }

    /**
     * 异常日志记录 按 ID 批量查询 id + 指定字段，返回 map ， id 为 key ， 指定字段为 value
     *
     * @param ids Set<Long> 数据ID
     * @param exp {@link QTabErrorLog} 指定单个字段
     * @return Map<Long, R> Map 对象集合
     */
    default <R> Map<Long, R> mapByIds(final Collection<Long> ids, final Expression<R> exp) {
        final Map<Long, R> map = new HashMap<>(ids.size());
        getQueryFactory()
                .select(table.id, exp)
                .from(table)
                .where(table.id.in(Sets.newHashSet(ids)))
                .fetch()
                .forEach(tuple -> {
                    if (Objects.nonNull(tuple.get(exp))) {
                        map.put(tuple.get(table.id), tuple.get(exp));
                    }
                });
        return map;
    }

    /**
     * 异常日志记录 按 ID 批量查询 id + 指定字段，返回 map ， id 为 key ， 指定字段投影到 {@link TabErrorLog}
     *
     * @param ids  {@link Long} 数据ID
     * @param exps {@link Expression} 指定多个字段
     * @return Map<Long, TabErrorLog> Map 对象集合
     */
    default Map<Long, TabErrorLog> mapByIds(final Collection<Long> ids, final Expression<?>... exps) {
        return getQueryFactory()
                //.select(Projections.bean(TabErrorLog.class, ObjectArrays.concat(table.id, exps))) // 如果 exps 带了 id 这里再加一个 id 查询会报错 ：  Multiple entries with same key
                .select(Projections.bean(TabErrorLog.class, exps))
                .from(table)
                .where(table.id.in(Sets.newHashSet(ids)))
                .fetch()
                .stream()
                .collect(Collectors.toMap(TabErrorLog::getId, row -> row));
    }

    /**
     * 异常日志记录 按 ID 批量查询 id + 指定字段，返回 map ， id 为 key ， 指定字段投影到 Class<R>
     *
     * @param ids  {@link Long} 数据ID
     * @param exps {@link Expression} 指定多个字段
     * @return Map<Long, R> Map 对象集合
     */
    default <R> Map<Long, R> mapByIds(final Collection<Long> ids, final Class<R> clazz, final Expression<?>... exps) {
        return getQueryFactory()
                //.select(table.id, Projections.bean(clazz, ObjectArrays.concat(table.id, exps))) //  如果 exps 带了 id 这里再加一个 id 查询会报错 ：  Multiple entries with same key
                .select(table.id, Projections.bean(clazz, exps))
                .from(table)
                .where(table.id.in(Sets.newHashSet(ids)))
                .fetch()
                .stream()
                .collect(Collectors.toMap(tuple -> tuple.get(table.id), tuple -> Objects.requireNonNull(tuple.get(1, clazz))));
    }

    /**
     * 异常日志记录 求总数
     *
     * @param where IWhere.QdslWhere 查询条件
     * @return long 总数
     */
    default long count(final IWhere.QdslWhere where) {
        return getQueryFactory()
                .select(table.id.count())
                .from(table)
                .where(where.toPredicate())
                .fetch()
                .stream()
                .findFirst()
                .orElse(0L);
    }

    /**
     * 异常日志记录 按条件分页查询， 仅返回 id 字段，用于分页查询优化
     *
     * @param condition {@link TabErrorLog} 查询条件
     * @param page      {@link Page} 分页
     * @return {@link QueryResults<Long>} 分页结果集
     */
    default QueryResults<Long> pageIds(final TabErrorLog condition, final Page page) {
        final long count = count(condition.where());
        if (count == 0L) {
            return QueryResults.emptyResults();
        }
        final List<Long> list = getQueryFactory()
                .select(table.id)
                .from(table)
                .where(condition.where().toArray())
                .orderBy(condition.qdslOrderBy())
                .offset(page.offset())
                .limit(page.limit())
                .fetch();
        return new QueryResults<>(list, (long) page.limit(), (long) page.offset(), count);
    }

    /**
     * 异常日志记录 按条件分页查询
     *
     * @param condition {@link TabErrorLog} 查询条件
     * @param page      {@link Page} 分页
     * @return {@link QueryResults<TabErrorLog>} 分页结果集
     */
    default QueryResults<TabErrorLog> page(final TabErrorLog condition, final Page page) {
        { // 直接查询
            // return getQueryFactory()
            //        .selectFrom(table)
            //        .where(condition.where().toArray())
            //        .offset(page.offset())
            //        .limit(page.limit())
            //        .orderBy(condition.qdslOrderBy())
            //        .fetchResults();
        }
        { // 查询优化版
            final long count = count(condition.where());
            if (count == 0L) {
                return QueryResults.emptyResults();
            }
            final List<Long> ids = listIds(page.offset(), page.limit(), condition); // 先查 id，避免回表，减少分页数据归集时间，大宽表或大量数据的情况下优化比较明显，分页页码越往后优化越明显
            final List<TabErrorLog> list = listByIds(ids); // 再按 id 批量查询
            return new QueryResults<>(list, (long) page.limit(), (long) page.offset(), count);
        }
    }

    /**
     * 异常日志记录 按条件分页查询
     *
     * @param condition {@link TabErrorLog} 查询条件
     * @param page      {@link Page} 分页
     * @param exps      {@link Expression} 指定返回字段
     * @return {@link QueryResults<TabErrorLog>} 分页结果集
     */
    default QueryResults<TabErrorLog> page(final TabErrorLog condition, final Page page, final Expression<?>... exps) {
        final long count = count(condition.where());
        if (count == 0L) {
            return QueryResults.emptyResults();
        }
        final List<Long> ids = listIds(page.offset(), page.limit(), condition); // 先查 id，避免回表，减少分页数据归集时间，大宽表或大量数据的情况下优化比较明显，分页页码越往后优化越明显
        final List<TabErrorLog> list = listByIds(ids, exps); // 再按 id 批量查询
        return new QueryResults<>(list, (long) page.limit(), (long) page.offset(), count);
    }

    /**
     * 异常日志记录 按条件分页查询，投影到 VO 类
     *
     * @param condition {@link TabErrorLog} 查询条件
     * @param page      {@link Page} 分页
     * @param clazz     {@link Class} 投影 VO 类
     * @return {@link QueryResults<TabErrorLog>} 分页结果集
     */
    default <T extends TabErrorLog> QueryResults<T> page(final TabErrorLog condition, final Page page, final Class<T> clazz) {
        return page(condition, page, clazz, TabErrorLog.allColumnAppends());
    }

    /**
     * 异常日志记录 按条件分页查询，查询指定字段，投影到 VO 类
     *
     * @param condition {@link TabErrorLog} 查询条件
     * @param page      {@link Page} 分页
     * @param clazz     {@link Class} 投影 VO 类
     * @param exps      {@link Expression} 指定返回字段
     * @return {@link QueryResults<TabErrorLog>} 分页结果集
     */
    default <T extends TabErrorLog> QueryResults<T> page(final TabErrorLog condition, final Page page, final Class<T> clazz, final Expression<?>... exps) {
        final long count = count(condition.where());
        if (count == 0L) {
            return QueryResults.emptyResults();
        }
        final List<Long> ids = listIds(page.offset(), page.limit(), condition); // 先查 id，避免回表，减少分页数据归集时间，大宽表或大量数据的情况下优化比较明显，分页页码越往后优化越明显
        final List<T> list = listByIds(ids, clazz, exps); // 再按 id 批量查询
        return new QueryResults<>(list, (long) page.limit(), (long) page.offset(), count);
    }

    /**
     * 异常日志记录 按条件查询返回 id ，默认最大返回行数为 1000 ，返回行数过大时请使用分页查询
     *
     * @param condition {@link TabErrorLog} 查询条件
     * @return {@link List<Long>} ID集合
     */
    default List<Long> listIds(final TabErrorLog condition) {
        return listIds(Limit.L1000.value, condition);
    }

    /**
     * 异常日志记录 按条件查询返回 id ，必须指定返回行数，返回行数过大时请使用分页查询
     *
     * @param limit     {@link Long} 指定返回行数
     * @param condition {@link TabErrorLog} 查询条件
     * @return {@link List<Long>} ID集合
     */
    default List<Long> listIds(final long limit, final TabErrorLog condition) {
        return getQueryFactory()
                .select(table.id)
                .from(table)
                .where(condition.where().toArray())
                .orderBy(condition.qdslOrderBy())
                .limit(limit)
                .fetch();
    }

    /**
     * 异常日志记录 按条件查询返回 id ，必须指定返回行数，返回行数过大时请使用分页查询
     *
     * @param offset    {@link Long} 跳过行数
     * @param limit     {@link Long} 指定返回行数
     * @param condition {@link TabErrorLog} 查询条件
     * @return {@link List<Long>} ID集合
     */
    default List<Long> listIds(final long offset, final long limit, final TabErrorLog condition) {
        return getQueryFactory()
                .select(table.id)
                .from(table)
                .where(condition.where().toArray())
                .orderBy(condition.qdslOrderBy())
                .offset(offset)
                .limit(limit)
                .fetch();
    }

    /**
     * 异常日志记录 按 ID 批量查询
     *
     * @param ids {@link Long} 数据ID
     * @return {@link List<TabErrorLog>} 实体对象集合
     */
    default List<TabErrorLog> listByIds(final Collection<Long> ids) {
        final Map<Long, TabErrorLog> map = findAllById(Sets.newHashSet(ids)).stream().collect(Collectors.toMap(TabErrorLog::getId, TabErrorLog::cloneObject));
        // 保证返回的顺序与传入的顺序一致
        return ids.stream().map(map::get).filter(Objects::nonNull).collect(Collectors.toList());
    }

    /**
     * 异常日志记录 按 ID 批量查询 id + 指定字段，返回 list , 返回结果保持同传入的 id 顺序一致
     *
     * @param ids  {@link Long} 数据ID
     * @param exps {@link Expression} 指定多个字段
     * @return {@link List<TabErrorLog>} 实体对象集合
     */
    default List<TabErrorLog> listByIds(final Collection<Long> ids, final Expression<?>... exps) {
        final Map<Long, TabErrorLog> map = getQueryFactory()
                .select(Projections.bean(TabErrorLog.class, ObjectArrays.concat(table.id, exps)))
                .from(table)
                .where(table.id.in(Sets.newHashSet(ids)))
                .fetch()
                .stream()
                .collect(Collectors.toMap(TabErrorLog::getId, row -> row));
        // 保证返回的顺序与传入的顺序一致
        return ids.stream().map(map::get).filter(Objects::nonNull).collect(Collectors.toList());
    }

    /**
     * 异常日志记录 按 ID 批量查询 id + 指定字段，返回 list , 返回结果保持同传入的 id 顺序一致
     *
     * @param ids  {@link Long} 数据ID
     * @param exps {@link Expression} 指定多个字段
     * @return {@link List<R>} 实体对象集合
     */
    default <R> List<R> listByIds(final Collection<Long> ids, final Class<R> clazz, final Expression<?>... exps) {
        final Map<Long, R> map = getQueryFactory()
                .select(table.id, Projections.bean(clazz, ObjectArrays.concat(table.id, exps)))
                .from(table)
                .where(table.id.in(Sets.newHashSet(ids)))
                .fetch()
                .stream()
                .collect(Collectors.toMap(tuple -> tuple.get(table.id), tuple -> Objects.requireNonNull(tuple.get(1, clazz))));
        // 保证返回的顺序与传入的顺序一致
        return ids.stream().map(map::get).filter(Objects::nonNull).collect(Collectors.toList());
    }

    /**
     * 异常日志记录 按条件查询返回列表，默认最大返回行数为 1000 ，返回行数过大时请使用分页查询
     *
     * @param condition {@link TabErrorLog} 查询条件
     * @return {@link List<TabErrorLog>} 实体对象集合
     */
    default List<TabErrorLog> list(final TabErrorLog condition) {
        return list(Limit.L1000.value, condition);
    }

    /**
     * 异常日志记录 按条件查询返回列表，必须指定返回行数，返回行数过大时请使用分页查询
     *
     * @param limit     {@link Long} 指定返回行数
     * @param condition {@link TabErrorLog} 查询条件
     * @return {@link List<TabErrorLog>} 实体对象集合
     */
    default List<TabErrorLog> list(final long limit, final TabErrorLog condition) {
        return getQueryFactory()
                .selectFrom(table)
                .where(condition.where().toArray())
                .orderBy(condition.qdslOrderBy())
                .limit(limit)
                .fetch();
    }

    /**
     * 异常日志记录 按条件查询指定字段返回列表，必须指定返回行数，返回行数过大时请使用分页查询
     *
     * @param condition {@link TabErrorLog} 查询条件
     * @return {@link List<TabErrorLog>} 实体对象集合
     */
    default <R> List<R> list(final TabErrorLog condition, final Expression<R> exp) {
        return list(Limit.L1000.value, condition, exp);
    }

    /**
     * 异常日志记录 按条件查询指定字段返回列表，必须指定返回行数，返回行数过大时请使用分页查询
     *
     * @param limit     {@link Long} 指定返回行数
     * @param condition {@link TabErrorLog} 查询条件
     * @return {@link List<TabErrorLog>} 实体对象集合
     */
    default <R> List<R> list(final long limit, final TabErrorLog condition, final Expression<R> exp) {
        return getQueryFactory()
                .select(exp)
                .from(table)
                .where(condition.where().toArray())
                .orderBy(condition.qdslOrderBy())
                .limit(limit)
                .fetch();
    }

    /**
     * 异常日志记录 按条件查询返回列表，默认最大返回行数为 1000 ，返回行数过大时请使用分页查询
     *
     * @param condition {@link TabErrorLog} 查询条件
     * @param exps      {@link Expression} 指定返回字段
     * @return {@link List<TabErrorLog>} 实体对象集合
     */
    default List<TabErrorLog> list(final TabErrorLog condition, final Expression<?>... exps) {
        return list(Limit.L1000.value, condition, exps);
    }

    /**
     * 异常日志记录 按条件查询返回列表，必须指定返回行数，返回行数过大时请使用分页查询
     *
     * @param limit     {@link Long} 指定返回行数
     * @param condition {@link TabErrorLog} 查询条件
     * @param exps      {@link Expression} 指定返回字段
     * @return {@link List<TabErrorLog>} 实体对象集合
     */
    default List<TabErrorLog> list(final long limit, final TabErrorLog condition, final Expression<?>... exps) {
        return getQueryFactory()
                .select(Projections.bean(TabErrorLog.class, exps))
                .from(table)
                .where(condition.where().toArray())
                .orderBy(condition.qdslOrderBy())
                .limit(limit)
                .fetch();
    }

    /**
     * 异常日志记录 按条件查询返回列表，投影到指定 VO 类，默认最大返回行数为 1000 ，返回行数过大时请使用分页查询
     *
     * @param condition {@link TabErrorLog} 查询条件
     * @param clazz     {@link Class} 投影 VO 类
     * @return {@link List<TabErrorLog>} 实体对象集合
     */
    default <T extends TabErrorLog> List<T> list(final TabErrorLog condition, final Class<T> clazz) {
        return list(Limit.L1000.value, condition, clazz, TabErrorLog.allColumnAppends());
    }

    /**
     * 异常日志记录 按条件查询返回列表，投影到指定 VO 类，必须指定返回行数，返回行数过大时请使用分页查询
     *
     * @param limit     {@link Long} 指定返回行数
     * @param condition {@link TabErrorLog} 查询条件
     * @param clazz     {@link Class} 投影 VO 类
     * @return {@link List<TabErrorLog>} 实体对象集合
     */
    default <T extends TabErrorLog> List<T> list(final long limit, final TabErrorLog condition, final Class<T> clazz) {
        return list(limit, condition, clazz, TabErrorLog.allColumnAppends());
    }

    /**
     * 异常日志记录 按条件查询返回列表，投影到指定 VO 类，默认最大返回行数为 1000 ，返回行数过大时请使用分页查询
     *
     * @param condition {@link TabErrorLog} 查询条件
     * @param clazz     {@link Class} 投影 VO 类
     * @param exps      {@link Expression} 指定返回字段
     * @return {@link List<TabErrorLog>} 实体对象集合
     */
    default <T extends TabErrorLog> List<T> list(final TabErrorLog condition, final Class<T> clazz, final Expression<?>... exps) {
        return list(Limit.L1000.value, condition, clazz, exps);
    }

    /**
     * 异常日志记录 按条件查询返回列表，投影到指定 VO 类，必须指定返回行数，返回行数过大时请使用分页查询
     *
     * @param limit     {@link Long} 指定返回行数
     * @param condition {@link TabErrorLog} 查询条件
     * @param clazz     {@link Class} 投影 VO 类
     * @param exps      {@link Expression} 指定返回字段
     * @return {@link List<TabErrorLog>} 实体对象集合
     */
    default <T extends TabErrorLog> List<T> list(final long limit, final TabErrorLog condition, final Class<T> clazz, final Expression<?>... exps) {
        return getQueryFactory()
                .select(Projections.bean(clazz, exps))
                .from(table)
                .where(condition.where().toArray())
                .orderBy(condition.qdslOrderBy())
                .limit(limit)
                .fetch();
    }

    /**
     * 异常日志记录 循环查询列表，使用 id 正序排列，用于批处理数据，类似分页查询批量导出。
     * <pre>
     * 注意：
     *   这里必须使用 id 排序(正序|倒序)，否则会导致数据重复查询，也可能漏掉一部分数据。
     *   循环禁止包含在事务内，避免数据库大批量操作数据导致锁表，请使用 @Transactional(readOnly = true, propagation = Propagation.NEVER)
     *   如果业务允许，跑批时每一条数据都捕获异常且新起一个新事务 @Transactional(propagation = Propagation.REQUIRES_NEW)， 避免一条数据异常引起所有数据回滚
     * </pre>
     *
     * @param listConsumer List<TabErrorLog> 查询结果
     * @param where        IWhere.QdslWhere 查询条件
     * @param exps         Expression 查询字段
     */
    default void forEach(final Consumer<List<TabErrorLog>> listConsumer,
                         final IWhere.QdslWhere where,
                         final Expression<?>... exps
    ) {
        Long id = null;
        final int limit = Limit.L1000.value;
        List<TabErrorLog> list;
        final Expression<?>[] columns = Optional.ofNullable(exps)
                .filter(arr -> arr.length > 0)
                .orElseGet(TabErrorLog::allColumnAppends);
        do {
            list = getQueryFactory()
                    .select(Projections.bean(TabErrorLog.class, columns))
                    .from(table)
                    .where(Optional.ofNullable(id).map(table.id::gt).orElse(null))
                    .where(where.toPredicate())
                    .orderBy(table.id.asc())
                    .limit(limit)
                    .fetch();
            if (!list.isEmpty()) {
                id = Objects.requireNonNull(list.get(list.size() - 1).getId());
                listConsumer.accept(list);
            }
        } while (Objects.equals(list.size(), limit));
    }

}
